// 如何将多次相同key的修改合并为一次
let activeEffect
const data = {num: 0, num2: 0}
const bucket = new WeakMap()
// 新增
let effectStack = []

// 收集依赖
function track (target, key) {
  let depsMap = bucket.get(target)
  if (!depsMap) {
    bucket.set(target, depsMap = new Map())
  }
  let deps = depsMap.get(key)
  if (!deps) {
    depsMap.set(key, deps = new Set())
  }
  if (activeEffect) {
    deps.add(activeEffect)
    activeEffect.deps.push(deps)
  }
}

// 触发事件
function trigger (target, key) {
  const depsMap = bucket.get(target)
  if (!depsMap) return
  const effects = depsMap.get(key)
  const effectsToRun = new Set()
  effects.forEach(effect => {
    if (effect !== activeEffect) {
      effectsToRun.add(effect)
    }
  })
  // ==== 2.修改 
  // effectsToRun.forEach(effect => effect())
  effectsToRun.forEach(effect => {
    // 如果有调度器, 则将副作用函数加入到调度器中
    if (effect.options.schedule) {
      effect.options.schedule(effect)
    } else {
      effect()
    }
  })
}

// 代理
const obj = new Proxy(data, {
  get(target, key) {
    track(target, key)
    return target[key]
  },
  set(target, key, value) {
    target[key] = value
    trigger(target, key)
  }
})

function cleanup (effectFn) {
  for (let i = 0; i < effectFn.deps.length; i++) {
    const deps = effectFn.deps[i]
    deps.delete(effectFn)
  }
  effectFn.deps.length = 0
}

// ==== 1. 修改
function effect (fn, options = {}) {
  const effectFn = () => {
    cleanup(effectFn)
    activeEffect = effectFn
    effectStack.push(effectFn)
    fn()
    effectStack.pop()
    activeEffect = effectStack[effectStack.length - 1]
  }
  effectFn.deps = []
  effectFn.options = options
  effectFn()
}

// 案例, 可以看到, 以下的obj.num++执行了几次, 就会输出几次console
// effect(() => {
//   console.log(obj.num,'num')
// })

// obj.num++
// obj.num++


// 增加调度机制
let isFlushing = false
const jobQueue = new Set()

function flushJob() {
  const p = Promise.resolve()
  if (isFlushing) return
  isFlushing = true
  p.then(() => {
    jobQueue.forEach(job => job())
  }).finally(() => {
    isFlushing = false
  })
}

effect(() => {
  console.log(obj.num, 'num 结果')
  console.log(obj.num2, 'num2 结果')
}, {
  schedule(effect) {
    jobQueue.add(effect)
    flushJob()
  }
})

obj.num++
obj.num++
obj.num++
obj.num++
obj.num2++
obj.num2++
obj.num2++




/**
 * 解析:
 * 1. 在副作用函数上增加了一个options, 并在其上添加了schedule调度函数
 * 2. 在schedule中, 每次执行都jobQueue.add(effect), 但是由于Set天生具有去重性, 所以只会增加一次
 * 3. flushJob也会执行多次, 但是由于isFlushing的存在, 且在上次微任务执行完前, isFlushing根本不会变回false, 所以, 保证了
 * 它本质上只会执行一次!
 * 4. 等外层的effectsToRun.forEach执行完毕后, 就会执行微任务, 此时, obj.num的值只有一个, 只有一个最终值
 */